// 生产者消费者模型是一种经典的并发编程模型
// 解决生产者和消费者之间数据共享和同步的问题

// 阻塞队列的实现
#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <functional>



void Download()
{
    std::cout << "我是一个下载任务..." << std::endl;
    sleep(3); // 假设处理任务比较耗时
}


using task_t = std::function<void()>;
// 任务形式1
class Task
{
public:
    Task(){}
    Task(int x, int y):_x(x), _y(y)
    {
    }
    void Execute()
    {
        _result = _x + _y;
    }
    int X() { return _x; }
    int Y() { return _y; }
    int Result()
    {
        return _result;
    }
private:
    int _x;
    int _y;
    int _result;
};

const int defaultcap=5;//默认临界区资源的大小
//需要保证生产数据 和 消费数据都是原子操作的
template <class T>
class BlockQueue
{
    private:
    bool IsFull() {return _q.size()>=_cap;}
    bool IsEmpty(){return _q.empty();}
public:
    BlockQueue(int cap=defaultcap)
    :_cap(cap)
    {
        //初始化锁和条件变量
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_full_cond,nullptr);
        pthread_cond_init(&_empty_cond,nullptr);
    }
    //生产者新增数据
    void Enque(const T& data)
    {
        pthread_mutex_lock(&_mutex);
        //队列满时，无法新增数据，阻塞等待
        while(IsFull())
        {
            //生产线程需要等待
            _psleep_num++;
            std::cout<<"生产者，进入 休眠了："<<_psleep_num<<std::endl;
            //生产线程在条件变量等待队列（_full_cond)中挂起（阻塞），需要释放_mutex锁
            //其他线程争取锁，然后唤醒(_full_cond)的线程
            //没有唤醒，则当前线程在条件变量等待队列中阻塞
            //成功唤醒，当前线程还在临界区中，需要重新争取锁_mutex
            //争取锁成功，继续运行
            //争取锁失败，在锁上进行阻塞
            pthread_cond_wait(&_full_cond,&_mutex);
            //pthread_cond_wait函数可能失败，立即返回，造成伪唤醒，需要进行二次判断
            _psleep_num--;
        }
        //走到这里，则队列中一定有空间
        _q.push(data);

        //此时可以唤醒休眠中的消费者
        if(_csleep_num>0)
        {
            pthread_cond_signal(&_empty_cond);
            std::cout<<"唤醒消费者:"<<std::endl;
        }
        //pthread_cond_signal(&_empty_cond);
        pthread_mutex_unlock(&_mutex);
    }
    //消费者 取出数据
    T pop()
    {
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            _csleep_num++;
            std::cout<<"消费者，进入休眠："<<_csleep_num<<std::endl;
            pthread_cond_wait(&_empty_cond,&_mutex);
            _csleep_num--;
        }
        T data=_q.front();
        _q.pop();

        //唤醒生产者
        if(_psleep_num>0)
        {
            pthread_cond_signal(&_full_cond);
            std::cout<<"唤醒生产者:"<<std::endl;
        }

        pthread_mutex_unlock(&_mutex);
        return data;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_empty_cond);
        pthread_cond_destroy(&_full_cond);
    }
private:
    std::queue<T> _q;//临界 资源
    int _cap; // 阻塞队列容量
    pthread_mutex_t _mutex;
    // 如果将生产者和消费者在同一个条件变量下进行等待，那么唤醒时，需要判断是生产者还是消费者
    pthread_cond_t _full_cond;  // 队列满时，无法新增数据，在该条件变量下进行等待，生产者
    pthread_cond_t _empty_cond; // 队列为空时，无法取出数据，在该条件变量下进行等待，消费者
    int _psleep_num;//生产者休眠个数
    int _csleep_num;//消费者休眠个数
};


